/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.drawee.drawable;

import com.oszc.bbhmlibrary.utils.LogUtil;
import ohos.agp.components.Image;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.ColorFilter;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.media.image.PixelMap;

import javax.annotation.Nullable;

/**
 * A forwarding drawable class - the goal is to forward (delegate) drawable functionality to an
 * inner drawable instance. ForwardingDrawable intercepts the public (and protected) methods of
 * {@link Element}, maintains local state if needed.
 *
 * <p>Design note: It would have been very helpful to re-use Android library classes like
 * DrawableContainer, LevelListDrawable etc. DrawableContainer is not directly subclassable, and the
 * others don't allow changing the member drawables.
 */
public class ForwardingDrawable extends  RootShapeElement
    implements TransformCallback, TransformAwareDrawable, DrawableParent,Element.OnChangeListener,Callback { //implements Drawable.Callback,
   private final String TAG = ForwardingDrawable.class.getSimpleName();

  /** The current drawable to be drawn by this drawable when drawing is needed */
  private Rect mBouns;

  @Override
  public void setBounds(int left, int top, int right, int bottom) {
    super.setBounds(left, top, right, bottom);
    mBouns = new Rect(left, top, right, bottom);
  }

  private @Nullable Element mCurrentDelegate;

  private final DrawableProperties mDrawableProperties = new DrawableProperties();

  protected TransformCallback mTransformCallback;

  public Image mImage;

  /**
   * Matrix used to store temporary transform. Drawables should be accessed on UI thread only, and
   * this matrix is used only as a temporary variable so it's safe to be static.
   */
  private static final Matrix sTempTransform = new Matrix();

  /**
   * Constructs a new forwarding drawable.
   *
   * @param drawable drawable that this forwarding drawable will forward to
   */
  public ForwardingDrawable(@Nullable Element drawable) {
    mCurrentDelegate = drawable;
    DrawableUtils.setCallbacks(mCurrentDelegate, this,this, this);
  }

  public void setImage(Image image) {
    this.mImage = image;
  }

  public void invalidateSelf() {
    if(getCallback()!=null){
      getCallback().onChange(this);
    }

    /*
    if (mImage != null) {
      mImage.addDrawTask(((component, canvas) -> drawToCanvas(canvas)));
    }
     */
  }



  /**
   * Sets a new drawable to be the delegate, and returns the old one (or null).
   *
   * <p>This method will cause the drawable to be invalidated.
   *
   * @param newDelegate
   * @return the previous delegate
   */
  public @Nullable Element setCurrent(@Nullable Element newDelegate) {
    Element previousDelegate = setCurrentWithoutInvalidate(newDelegate);
    invalidateSelf();
    return previousDelegate;
  }

  /**
   * As {@code setCurrent}, but without invalidating a drawable. Subclasses are responsible to call
   * {@code invalidateSelf} on their own.
   *
   * @param newDelegate
   * @return the previous delegate
   */
  protected @Nullable Element setCurrentWithoutInvalidate(@Nullable Element newDelegate) {
    Element previousDelegate = mCurrentDelegate;
    DrawableUtils.setCallbacks(previousDelegate, null,null, null);
    DrawableUtils.setCallbacks(newDelegate, null, null,null);
    DrawableUtils.setDrawableProperties(newDelegate, mDrawableProperties);
    DrawableUtils.copyProperties(newDelegate, this);
    DrawableUtils.setCallbacks(newDelegate, this, this,this);
    mCurrentDelegate = newDelegate;
    return previousDelegate;
  }

//  @Override
//  public int getOpacity() {
//    if (mCurrentDelegate == null) {
//      return PixelFormat.UNKNOWN;
//    }
//
//    return mCurrentDelegate.getOpacity();
//  }

  @Override
  public void setAlpha(int alpha) {
    mDrawableProperties.setAlpha(alpha);
    if (mCurrentDelegate != null) {
      mCurrentDelegate.setAlpha(alpha);
    }
  }

  @Override
  public void setColorFilter(ColorFilter colorFilter) {
    mDrawableProperties.setColorFilter(colorFilter);
    if (mCurrentDelegate != null && mCurrentDelegate instanceof RootShapeElement) {
      ((RootShapeElement)mCurrentDelegate).setColorFilter(colorFilter);
    }
  }

//  @Override
//  public void setDither(boolean dither) {
//    mDrawableProperties.setDither(dither);
//    if (mCurrentDelegate != null) {
//      mCurrentDelegate.setDither(dither);
//    }
//  }

//  @Override
//  public void setFilterBitmap(boolean filterBitmap) {
//    mDrawableProperties.setFilterBitmap(filterBitmap);
//    if (mCurrentDelegate != null) {
//      mCurrentDelegate.setFilterBitmap(filterBitmap);
//    }
//  }

  @Override
  public boolean setVisible(boolean visible, boolean restart) {
    
    final boolean superResult = super.setVisible(visible, restart);
    
    if (mCurrentDelegate == null) {
      return superResult;
    }
    return mCurrentDelegate.setVisible(visible, restart);
  }

  @Override
  public void setBounds(Rect bounds) {
      super.setBounds(bounds);
    if (mCurrentDelegate != null) {
      mCurrentDelegate.setBounds(bounds);
    }
  }

//  @Override
//  public @Nullable Drawable.ConstantState getConstantState() {
//    if (mCurrentDelegate == null) {
//      return super.getConstantState();
//    }
//
//    return mCurrentDelegate.getConstantState();
//  }

  @Override
  public boolean isStateful() {
    if (mCurrentDelegate == null) {
      return false;
    }
    return mCurrentDelegate.isStateful();
  }

//  @Override
//  protected boolean onStateChange(int[] state) {
//    if (mCurrentDelegate == null) {
//      return super.onStateChange(state);
//    }
//
//    return mCurrentDelegate.setState(state);
//  }
//
//  @Override
//  protected boolean onLevelChange(int level) {
//    if (mCurrentDelegate == null) {
//      return super.onLevelChange(level);
//    }
//
//    return mCurrentDelegate.setLevel(level);
//  }

  @Override
  public void drawToCanvas(Canvas canvas) {
    if (mCurrentDelegate != null) {
      mCurrentDelegate.drawToCanvas(canvas);
    }
  }

  @Override
  public int getIntrinsicWidth() {
    if (mCurrentDelegate == null) {
      super.getIntrinsicWidth();
    }

    if(mCurrentDelegate instanceof PixelMapElement){
      PixelMap pixelMap = ((PixelMapElement) mCurrentDelegate).getPixelMap();
      return pixelMap.getImageInfo().size.width;
    }

    if(mCurrentDelegate instanceof  RootShapeElement){
      return ((RootShapeElement) mCurrentDelegate).getIntrinsicWidth();
    }

    return super.getIntrinsicWidth();
  }

  @Override
  public int getIntrinsicHeight() {
    if (mCurrentDelegate == null) {
      return super.getIntrinsicHeight();
    }
    
    if(mCurrentDelegate instanceof PixelMapElement){
      PixelMap pixelMap = ((PixelMapElement) mCurrentDelegate).getPixelMap();
      int height = pixelMap.getImageInfo().size.height;
      LogUtil.error(TAG,"getIntrinsicHeight getBytesNumberPerRow:"+pixelMap.getBytesNumberPerRow()+"   height:"+height);
      return height;
    }

    if(mCurrentDelegate instanceof RootShapeElement){
      return ((RootShapeElement) mCurrentDelegate).getIntrinsicHeight();
    }
    return super.getIntrinsicHeight();
  }

  @Override
  public Element getCurrentElement() {
    return mCurrentDelegate;
  }

  /*
  @Override
  public boolean getPadding(Rect padding) {
    if (mCurrentDelegate == null) {
      return super.getPadding(padding);
    }

    return mCurrentDelegate.getPadding(padding);
  }

  @Override
  public Drawable mutate() {
    if (mCurrentDelegate != null) {
      mCurrentDelegate.mutate();
    }

    return this;
  }


  // DrawableParent methods

  @Override
  public Drawable setDrawable(@Nullable Drawable newDrawable) {
    return setCurrent(newDrawable);
  }

  @Override
  public @Nullable Drawable getDrawable() {
    return getCurrent();
  }

  // Drawable.Callback methods

  @Override
  public void invalidateDrawable(Drawable who) {
    invalidateSelf();
  }

  @Override
  public void scheduleDrawable(Drawable who, Runnable what, long when) {
    scheduleSelf(what, when);
  }

  @Override
  public void unscheduleDrawable(Drawable who, Runnable what) {
    unscheduleSelf(what);
  }
  */
  //  TransformAwareDrawable methods

  @Override
  public void setTransformCallback(TransformCallback transformCallback) {
    mTransformCallback = transformCallback;
  }

  protected void getParentTransform(Matrix transform) {
    if (mTransformCallback != null) {
      mTransformCallback.getTransform(transform);
    } else {
      transform.reset();
    }
  }

  @Override
  public void getTransform(Matrix transform) {
    getParentTransform(transform);
  }

  @Override
  public void getRootBounds(RectFloat bounds) {
    if (mTransformCallback != null) {
      mTransformCallback.getRootBounds(bounds);
    } else {
      bounds.left = getBounds().left;
      bounds.right = getBounds().right;
      bounds.top = getBounds().top;
      bounds.bottom = getBounds().bottom;
//      bounds.set(getMBounds());
    }
  }



  /**
   * Gets the transformed bounds of this drawable. Note: bounds are not cropped (otherwise they
   * would likely be the same as drawable's bounds).
   *
   * @param outBounds rect to fill with bounds
   */
  public void getTransformedBounds(RectFloat outBounds) {
    getParentTransform(sTempTransform);
    // IMPORTANT: {@code getMBounds} should be called after {@code getParentTransform},
    // because the parent may have to change our bounds.
//    outBounds.set(getMBounds());
    outBounds.left = getBounds().left;
    outBounds.right = getBounds().right;
    outBounds.top = getBounds().top;
    outBounds.bottom = getBounds().bottom;
    sTempTransform.mapRect(outBounds);
  }

  @Override
  public Element setDrawable(Element newDrawable) {
    return setCurrent(newDrawable);
  }

  @Override
  public Element getDrawable() {
    return mCurrentDelegate;
  }

//  @Override
//  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
//  public void setHotspot(float x, float y) {
//    if (mCurrentDelegate != null) {
//      mCurrentDelegate.setHotspot(x, y);
//    }
//  }

  /*
  public void createNativePtr(Object obj){
  }

   */


  @Override
  public void onChange(Element element) {
    invalidateSelf();
  }

  @Override
  public void invalidateDrawable(Element who) {
    invalidateSelf();
  }

  @Override
  public void scheduleDrawable(Element who, Runnable what, long when) {
    scheduleSelf(what,when);

  }

  @Override
  public void unscheduleDrawable(Element who, Runnable what) {
    unscheduleSelf(what);

  }
}
